package pipe

import (
	"gitee.com/injoyai/goutil/maps"
	"gitee.com/injoyai/goutil/net/conn/v2"
	"gitee.com/injoyai/goutil/net/tcp/v2"
	"gitee.com/injoyai/goutil/oss/io"
	"log"
	"strings"
	"time"
)

func NewProxy(sn string) *Proxy {
	p := &Proxy{
		sn:        sn,
		conn:      maps.NewSafe(),
		closeFunc: nil,
		dealFunc:  nil,
		redirect:  maps.NewSafe(),
		printFunc: io.PrintWithASCII,
		writeFunc: nil,
		readFunc:  io.ReadWithAll,
	}
	p.redirectFunc = p.getAddr
	return p
}

// Proxy 代理请求管理
type Proxy struct {
	sn   string     //
	conn *maps.Safe //代理的客户端连接

	closeFunc    func(key, msg string)               //关闭事件函数
	dealFunc     func(key string, msg *conn.Message) //处理数据函数
	redirect     *maps.Safe                          //重定向
	redirectFunc func(string) string                 //自定义重定向
	debug        bool
	printFunc    func(string, *io.Message)
	writeFunc    func([]byte) []byte
	readFunc     func(*io.Client) ([]byte, error)
}

func (this *Proxy) Debug(b ...bool) {
	this.debug = !(len(b) > 0 && !b[0])
}

func (this *Proxy) SetPrintFunc(fn func(string, *io.Message)) *Proxy {
	this.printFunc = fn
	return this
}

func (this *Proxy) SetWriteFunc(fn func([]byte) []byte) *Proxy {
	this.writeFunc = fn
	return this
}

func (this *Proxy) SetReadFunc(fn func(*io.Client) ([]byte, error)) *Proxy {
	this.readFunc = fn
	return this
}

// getAddr 获取重定向之后的地址
func (this *Proxy) getAddr(addr string) string {
	x := this.redirect.GetVar("*").String(addr)
	return this.redirect.GetVar(addr).String(x)
}

// setRedirect 重定向,*表示重定向全部,用默认重定向函数是生效
func (this *Proxy) setRedirect(oldAddr, newAddr string) *Proxy {
	if len(newAddr) > 0 {
		if !strings.Contains(newAddr, ":") {
			newAddr += ":80"
		}
		this.redirect.Set(oldAddr, newAddr)
	}
	return this
}

func (this *Proxy) setRedirectFunc(fn func(string) string) *Proxy {
	this.redirectFunc = fn
	return this
}

func (this *Proxy) setCloseFunc(fn func(key, msg string)) *Proxy {
	this.closeFunc = fn
	return this
}

func (this *Proxy) setDealFunc(fn func(key string, msg *conn.Message)) *Proxy {
	this.dealFunc = fn
	return this
}

func (this *Proxy) DecodeAndDo(bytes []byte) error {
	msg, err := decodeMsg(bytes)
	if err != nil {
		return err
	}
	return this.Do(msg)
}

func (this *Proxy) Do(m *Message) (err error) {
	defer Recover(&err)
	if this.redirectFunc != nil && m.Type != Close {
		m.Addr = this.redirectFunc(m.Addr)
	}
	switch m.Type {
	case Connect:
		_, err = this.connect(m.Key, m.Addr)
	case Write:
		err = this.write(m.Key, m.Addr, m.Data)
	case Close:
		this.close(m.Key)
	}
	return
}

// SetConn 设置连接
func (this *Proxy) SetConn(key string, conn *tcp.Client) {
	if conn == nil {
		return
	}
	i := this.conn.GetInterface(key)
	if i != nil {
		i.(*tcp.Client).Close()
	}
	this.conn.Set(key, conn)
}

func (this *Proxy) connect(key string, addr string) (cli *tcp.Client, err error) {
	i := this.conn.GetInterface(key)
	if i == nil || i.(*tcp.Client).Err() != nil {
		c, err := tcp.Dial(addr)
		if err == nil {
			log.Printf("[连接][pipe][%s] 连接服务成功...", addr)
			c.SetPrintFunc(func(s string, msg *io.Message) {
				log.Printf("[%s][pipe][%s] %s\n", s, msg.Key(), msg.String())
			})
			c.SetTimeout(time.Second * 10)
			c.SetKey(key)
			c.SetPrintFunc(this.printFunc)
			c.Debug(this.debug)
			c.SetWriteFunc(this.writeFunc)
			c.SetReadFunc(this.readFunc)
			c.SetCloseFunc(func(msg *conn.Message) {
				if this.closeFunc != nil {
					this.closeFunc(key, msg.String())
				}
				log.Printf("[错误][%s][%s] %s", this.sn, msg.Key(), msg.String())
				this.conn.Del(key)
			})
			c.SetDealFunc(func(msg *conn.Message) {
				if this.dealFunc != nil {
					this.dealFunc(key, msg)
				}
			})
			this.conn.Set(key, c)
		}
		return c, err
	}
	return i.(*tcp.Client), nil
}

func (this *Proxy) write(key string, addr, msg string) error {
	c, err := this.connect(key, addr)
	if err != nil {
		return err
	}
	return c.WriteString(msg)
}

func (this *Proxy) close(key string) {
	i := this.conn.GetInterface(key)
	if i != nil {
		i.(*tcp.Client).Close()
		this.conn.Del(key)
	}
}

func (this *Proxy) closeAll() {
	this.conn.Range(func(key, value interface{}) bool {
		_ = value.(*tcp.Client).Close()
		return true
	})
	this.conn = maps.NewSafe()
}
